from eval to getattr hasattr setattr

现实中的问题:我手中有一个列表[(’请求网页’,’网页1’),(’解析网页’,’网页1’, (‘保存网页’, ‘网页1’),’请求网页’,’网页2’),(’解析网页’,’网页2’, (‘保存网页’, ‘网页2’),… ],想将列表中的事情从头到尾都干一遍。

  1. 首先,定义一个类

    1
    2
    3
    4
    5
    6
    7
    class Spider:
    def request(self, page):
    print('请求网页:', page)
    def parse(self, page):
    print('解析网页:', page)
    def save(self, page):
    print('保存网页:', page)
  2. 然后我们想的是,如何复用Spider里的方法
    许多人的做法是直接使用eval
    做法如下

    1
    2
    3
    4
    5
    todo_lst = [('request', 'www.exobrain.online'), ('parse', 'www.exobrain.online'), ('save', 'www.exobrain.online'),('request', 'www.exobrain.online/categories/'), ('parse', 'www.exobrain.online/categories/'), ('save', 'www.exobrain.online/categories/'), ('方法测试1','www.exobrain.online/categories/')]
    spider = Spider()
    for item in todo_lst:
    string = 'spider.' + item[0]+'("'+item[1]+'")'
    eval(string)

出来了结果

1
2
3
4
5
6
7
8
9
请求网页: www.exobrain.online
解析网页: www.exobrain.online
保存网页: www.exobrain.online
请求网页: www.exobrain.online/categories/
解析网页: www.exobrain.online/categories/
保存网页: www.exobrain.online/categories/

...
AttributeError: 'Spider' object has no attribute '错误的方法测试1'(报错:部分)

我们看到了报错,自然可以用try except处理。
but,eval is evil, it’s not suggested. If you use eval, python is no longer elegant.
So, we introduce the inbuit methods “getattr, hasattr, setattr
(参考自https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string
不得不感慨国内外的差距,人家11years前解决的问题,我们到现在还有好多程序员处于问题之中,还在大量使用eval()。)

使用getattr代替eval, 将for部分注释掉重写如下

1
2
3
4
5
6
7
8
9
10
11
todo_lst = [('request', 'www.exobrain.online'), ('parse', 'www.exobrain.online'), ('save', 'www.exobrain.online'),('request', 'www.exobrain.online/categories/'), ('parse', 'www.exobrain.online/categories/'), ('save', 'www.exobrain.online/categories/'), ('方法测试1','www.exobrain.online/categories/')]
spider = Spider()
# for item in todo_lst:
# string = 'spider.' + item[0]+'("'+item[1]+'")'
# eval(string)

for item in todo_lst:
if not hasattr(spider, item[0]):
print('Spider类里不存在方法:', item[0])
continue
getattr(spider, item[0])(item[1])

我们看到了漂亮的结果,有没有发现上面的hasattr+getattr的代码比eval的代码更加优雅。
I choose getattr instead of eval just because of elegance. 😃

1
2
3
4
5
6
7
请求网页: www.exobrain.online
解析网页: www.exobrain.online
保存网页: www.exobrain.online
请求网页: www.exobrain.online/categories/
解析网页: www.exobrain.online/categories/
保存网页: www.exobrain.online/categories/
Spider类里不存在方法: 方法测试1
  1. 动态将函数添加到类或者对象里面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def method_added(page):
    print('调用method_added函数',page)

    # 将method_added 注册到spider对象里
    setattr(spider, 'method_added', method_added)
    # 测试method_added是否添加到spider对象里
    print(hasattr(spider, 'method_added'))
    # >True 返回值为True, 添加成功
    getattr(spider, 'method_added')('www.exobrain.online')
    # > 调用method_added函数 www.exobrain.online


    def method_added2(self, page):
    print('调用method_added2函数',page)

    # 将method_added2 注册到Spider类里
    setattr(Spider, 'method_added2', method_added2)
    # 测试method_added是否添加到spider对象里
    print(hasattr(spider, 'method_added2'))
    # >True 返回值为True, 添加成功
    getattr(spider, 'method_added2')('www.exobrain.online')
    # > 调用method_added函数 www.exobrain.online
  2. 对于函数调用怎么办?
    我们可以使用locals 和 globals。locals() 和 globals() 是python的两个内置函数,通过它们可以一字典的方式访问局部和全局变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def foo():
    print "foo"

    def bar():
    print "bar"

    func_list = ["foo","bar"]

    for func in func_list:
    locals()[func]()
    # >foo
    # >bar

    for func in func_list:
    globals()[func]()
    # >foo
    # >bar

参考:
https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string